#if defined(PLC_IS_APP) || defined(PLC)

#include "plc_app.h"
#include "iec_types_all.h"

#define PLC_LOC_BUF(name) PLC_LOC_CONCAT(name, _BUF)
#define PLC_LOC_ADDR(name) PLC_LOC_CONCAT(name, _ADDR)
#define PLC_LOC_DSC(name) PLC_LOC_CONCAT(name, _LDSC)

#define __LOCATED_VAR(type, name, lt, lsz, io_proto, ...)       \
    type PLC_LOC_BUF(name);                                     \
    type *name = &(PLC_LOC_BUF(name));                          \
    const uint32_t PLC_LOC_ADDR(name)[] = {__VA_ARGS__};             \
    const plc_loc_dsc_t PLC_LOC_DSC(name) =                     \
        {                                                       \
            .v_buf = (void *)&(PLC_LOC_BUF(name)),              \
            .v_type = PLC_LOC_TYPE(lt),                         \
            .v_size = PLC_LOC_SIZE(lsz),                        \
            .a_size = sizeof(PLC_LOC_ADDR(name)) / sizeof(uint32_t), \
            .a_data = &(PLC_LOC_ADDR(name)[0]),                 \
            .proto = io_proto};

#include "LOCATED_VARIABLES.h"
#include "rte_abi.h"

#undef __LOCATED_VAR

#define __LOCATED_VAR(type, name, ...) &(PLC_LOC_DSC(name)),
plc_loc_tbl_t plc_loc_table[] =
    {
#include "LOCATED_VARIABLES.h"
};
#undef __LOCATED_VAR

#define PLC_LOC_TBL_SIZE (sizeof(plc_loc_table) / sizeof(plc_loc_dsc_t *))

#ifndef PLC_MD5
#error "PLC_MD5 must be defined!!!"
#endif

#define PLC_MD5_STR(a) #a
#define PLC_MD5_STR2(a) PLC_MD5_STR(a)
// #ifdef PLC_IS_APP
// __attribute__((section(".plc_md5_sec"))) char plc_md5[] = PLC_MD5_STR2(PLC_MD5);
// //App ABI, placed at the .text end
// __attribute__((section(".plc_check_sec"))) char plc_check_md5[] = PLC_MD5_STR2(PLC_MD5);
// #else
const char plc_md5[] = PLC_MD5_STR2(PLC_MD5);
const char plc_check_md5[] = PLC_MD5_STR2(PLC_MD5);
// #endif
// Linker added symbols

extern uint32_t _plc_data_loadaddr, _plc_data_start, _plc_data_end, _plc_bss_end, _plc_sstart;

extern app_fp_t _plc_pa_start, _plc_pa_end;
extern app_fp_t _plc_ia_start, _plc_ia_end;
extern app_fp_t _plc_fia_start, _plc_fia_end;

void fake_start(void);

extern void config_init(void);

extern void RegisterVariableForce(unsigned int idx, void *force);

unsigned int GetRetainSize(void);

// App ABI, placed at the .text start
plc_data_t data_t;
#ifdef PLC_IS_APP
__attribute__((section(".plc_app_abi_sec"))) const plc_app_abi_t plc_app =
#else
const plc_app_abi_t plc_app =
#endif
    {
#if defined(PLC_IS_APP) && !(defined(windows_x86) || defined(SYLIXOS) || defined(PLC))
        .sstart = (uint32_t *)&_plc_sstart,
        .entry = fake_start,
        // Startup interface
        .data_loadaddr = &_plc_data_loadaddr,

        .data_start = &_plc_data_start,
        .data_end = &_plc_data_end,

        .bss_end = &_plc_bss_end,

        .pa_start = &_plc_pa_start,
        .pa_end = &_plc_pa_end,

        .ia_start = &_plc_ia_start,
        .ia_end = &_plc_ia_end,

        .fia_start = &_plc_fia_start,
        .fia_end = &_plc_fia_end,
#endif
        // Must be run on compatible RTE
        .rte_ver_major = 2,
        .rte_ver_minor = 0,
        .rte_ver_patch = 0,
        .hw_id = HWMODEL,
        // IO manager interface
        //  #ifdef PLC_IS_APP
        .l_tab = &plc_loc_table[0],
        .l_sz = PLC_LOC_TBL_SIZE,
        // #endif
        // App interface
        .check_id = plc_check_md5,
        .id = plc_md5,
        .app_name = SWNAME,
        .buildnumber = 0,
        .start = startPLC,
        .stop = stopPLC,
        .run = runPLC,
        .config_init = config_init,
        .dbg_resume = resumeDebug,
        .dbg_suspend = suspendDebug,

        .dbg_data_get = GetDebugData,
        .dbg_data_free = FreeDebugData,

        .dbg_vars_reset = ResetDebugVariables,
        .dbg_var_register = RegisterDebugVariable,
        .dbg_set_force = RegisterVariableForce,

        .GetRetainSize = GetRetainSize,
        .data = &data_t,
};

int rte_create_task(const char *name, uint32_t t, void *fn)
{
    if (plc_rte->create_task(name, t, fn))
    {
        plc_rte->log_error("create task failed.");
    }
    else
    {
        plc_rte->log_debug("create task success.");
    }
}

int rte_create_timer_task(const char *name, uint32_t id, uint32_t t, void *fn)
{
    if (plc_rte->create_timer_task(name, id, t, fn))
    {
        plc_rte->log_error("create_timer_task failed.");
    }
    else
    {
        plc_rte->log_debug("create_timer_task success.");
    }
}

int rte_create_exti_task(const char *name, uint32_t id, u8 rise, u8 fall, void *fn)
{
    if (plc_rte->create_exti_task(name, id, rise, fall, fn))
    {
        plc_rte->log_error("create exti_task failed.");
    }
    else
    {
        plc_rte->log_debug("create_exti_task success.");
    }
}

#endif
